package cn.retalsystem.advice;

import cn.retalsystem.util.Cache;
import cn.retalsystem.util.CacheType;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;

/**
 * @Author: 官宝星
 * @Date: 2019/11/4 14:37
 * @Version 1.0
 * @描述：
 */
@Data
public class RedisAdvice {
    RedisTemplate redisTemplate;

    private static final Logger logger=Logger.getLogger(RedisAdvice.class);

    public Object redisAround(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.debug("redis缓存拦截方法");
        //获取目标对象
        Class<?> target = joinPoint.getTarget().getClass();
        logger.debug("redis中与目标对象匹配的key："+redisTemplate.keys(target.getSimpleName()+"*"));
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        logger.debug("方法中的参数："+Arrays.toString(args));
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        //获取方法对象
        Method method=null;
        if(args==null||args.length==0){
            method=target.getMethod(methodName);
        }else {
            //获取参数的Class数组
            Class[] cls=new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                cls[i]=args[i].getClass();
            }
            method=target.getMethod(methodName,cls);
        }
        logger.debug("获取方法对象："+methodName);
        //获取方法上的注解
        Cache cache = method.getAnnotation(Cache.class);
        //判断是否为空，为空则不进行任何增强
        logger.debug("判断是否有注解@Cache");
        if(cache==null){
            logger.debug("没有注解，执行原方法");
            //执行目标方法
            return execute(joinPoint);
        }else{
            logger.debug("有注解@Cache，执行redis缓存操作");
            CacheType type = cache.type();
            if(type==CacheType.SELECT){
                logger.info("对redis进行查询操作"+type);
                //获取key，key值为目标类名+方法名+参数名
                String key=target.getSimpleName()+methodName;
                for (Object arg : args) {
                    key+= JSON.toJSONString(arg);
                }
                //在redis中查询key，若存在则直接返回，不存在则进行数据库查询并存入redis
                Object o = redisTemplate.opsForValue().get(key);
                if(o==null){
                    logger.info("redis中不存在key:["+key+"],执行sql查询并存入redis");
                    Object obj = execute(joinPoint);
                    redisTemplate.opsForValue().set(key,obj);
                    return obj;
                }else {
                    logger.info("redis中存在key:["+key+"]，返回对象");
                    return o;
                }
            }else if(type==CacheType.UPDATE){

                logger.info("对redis进行修改操作"+type);
                String pattern=target.getSimpleName()+"*";
                logger.debug("获取redis中匹配的keys："+pattern);
                Set keys = redisTemplate.keys(pattern);
                logger.debug("获取当前Serive匹配的key集合");
                redisTemplate.delete(keys);
                logger.info("redis删除的key："+keys);
                String[] dkeys = cache.delKey();
                logger.debug("注解中的delKeys:"+Arrays.toString(dkeys)+",数量:"+dkeys.length);
                if(dkeys[0].isEmpty()){
                    logger.debug("没有额外指定key需要删除");
                }
                else{
                    for (String dkey : dkeys) {
                        Set ks = redisTemplate.keys(dkey + "*");
                        redisTemplate.delete(ks);
                        logger.info("删除注解中指定key:"+ks);
                    }
                }
                logger.debug("执行修改操作");
                Object obj = execute(joinPoint);
                logger.debug("修改完成");
                return obj;
            }
        }
        return null;
    }

    private Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw throwable;
        }
    }
}
